package org.ycsoft.PathComputation;

import java.io.FileOutputStream;
import java.io.UnsupportedEncodingException;
import java.util.*;

/**
 * Contains the shortest paths tree of a single source stop
 * @author allad
 *
 */
public class ShortestPathTree implements MetroidEntity{
	
	private Stop source;
	//private int totalStops = 0; //total number of stops
	Map<Stop,Stop> previousStop = new HashMap<Stop,Stop>();
	Map<Stop,Integer>  distances = new HashMap<Stop,Integer>();  //distances to root
	Map<Stop,Line> backEdgesLines = new HashMap<Stop, Line>(); //lines to which back edges belong to
	
	public ShortestPathTree(Stop root,StopDB stopsDB){
		if(root == null)
			throw new NullPointerException("ShortestPath() : null root stop.");
		this.source = root;
		previousStop.put(source, Commons.STOP_NULL); //the previous stop of the root is NUll
		for(Stop stop : stopsDB.getStopsSet()){
			if(stop.equals(root)){
				distances.put(root, 0);
			}
			else {
				distances.put(stop, Commons.INFINITY);
			}
			backEdgesLines.put(stop, Commons.LINE_NULL);
		}
	}
	
	/**
	 * Put the previous stop of a stop in this shortest paths tree
	 * @param stop 
	 * @param previous
	 */
	public void putPrevious(Stop stop,Stop previous){
		if(stop != source){
			previousStop.put(stop, previous);
		}
	}
	
	/**
	 * Update the known distance to the given stop
	 * @param stop Stop whose distance to update
	 * @param distance Currently known distance
	 */
	public void putDistance(Stop stop,int distance){
		if(stop != source){
			distances.put(stop, distance);
		}
	}
	
	/**
	 * Updates the line to which the back edge of this stop belongs to
	 * @param stop Stop whose back edge to update
	 * @param line Back edge line
	 */
	public void putBackEdgeLine(Stop stop,Line line){
		if(stop != source){
			backEdgesLines.put(stop, line);
		}
	}
	
	/**
	 * Returns the current know distance to source of the given stop.
	 * If the distance for that stop is not known, returns INFINITY
	 * @param stop
	 * @return The distance to the root if known or INFINITY otherwise
	 */
	public int getDistance(Stop stop){
		if(distances.containsKey(stop)){
			return distances.get(stop);
		}
		else return Commons.INFINITY;
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString(){
		StringBuilder sb = new StringBuilder();
		
		sb.append("Root - "+source+"\n");
		
		for(Stop stop : previousStop.keySet()){
			if(stop == null){
				sb.append("null+\n");
			}
			else sb.append(stop.toString()+"|"+stop.getStopId()+" - "+previousStop.get(stop)+","+distances.get(stop)+" - "+backEdgesLines.get(stop)+"\n");
		}
		
		return sb.toString();
	}

	public String toMetroid() {
		StringBuilder sb1 = new StringBuilder();
		StringBuilder sb = new StringBuilder();
		sb1.append("tree "+source.getStopId()+Commons.LINE_SEPARATOR);
		
		//classement des stops par ordre croissant d'id dans un tableau
		int total = previousStop.size();
		Stop[] temp = new Stop[total];
		for(Stop stop : previousStop.keySet()){
			int id = stop.getStopId();
			temp[id%total] = stop;
		}
		
		//écriture lignes
		for(int i=0;i<temp.length;i++){
			Stop stop = temp[i];
			//TODO cas des trajets a pied
			sb.append(String.format(Commons.formatNumber(stop.getStopId(),Commons.ID_DIGITS_NB)+":"+Commons.formatNumber(previousStop.get(stop).getStopId(),Commons.ID_DIGITS_NB)+","+Commons.formatNumber(distances.get(stop),Commons.LENGTH_DIGITS_NB)+","+Commons.formatNumber(backEdgesLines.get(stop).getLineId(), Commons.ID_DIGITS_NB)+",n"));
			sb.append(Commons.LINE_SEPARATOR);
		}
		
		byte[] tmp = Commons.compressString(sb.toString());
		String data;
		try{
			data = new String(tmp,Commons.DEFAULT_ENCODING);
		}catch(UnsupportedEncodingException e){
			data = new String(tmp);
		}
		
		sb1.append("zip "+tmp.length+Commons.LINE_SEPARATOR);
		sb1.append(data);
		sb1.append(Commons.LINE_SEPARATOR);
		sb1.append("end"+Commons.LINE_SEPARATOR);

		return sb1.toString();
	}

}
